/* 
 * File:   CPU6502.h
 * Author: Kai_Jiang
 *
 * Created on January 21, 2015, 1:25 PM
 */

#ifndef CPU6502_H
#define	CPU6502_H
#include <memory.h>
#include <iostream>
using namespace std;

#define	MaxSize 0xFFFF
typedef void (*FUN) ();
typedef char int8;
typedef short int16;
typedef int int32;
typedef long int64;

typedef unsigned char unint8;
typedef unsigned short unint16;
typedef unsigned int unint32;
typedef unsigned long unint64;


unint16 PC;
int8 A;
int8 X;
int8 Y;
int8* SP;
int8* Memory;

//cpu cycles counter
unint8 Cycles;

//THE STATUS REGISTER
int8 Z; //Zero flag
int8 I; //interrupt enable/disable flag
int8 D; //decimal mode
int8 B; //this is set when a software interrupt (BRK instruction) is executed.
int8 V; //Overflow flag
int8 S; //Sign flag
int8 C; //Carry flag


FUN* TranslatedBlock;

void reset() {
    PC = 0;
    Z = 0;
    I = 0;
    D = 0;
    B = 0;
    V = 0;
    S = 0;
    C = 0;
    A = 0;
    X = 0;
    Y = 0;

    Cycles = 0;
    if (Memory != 0) delete[] Memory;
    Memory = new int8[MaxSize];
    SP = Memory + 0x01FF;
    if (TranslatedBlock != 0) delete[] TranslatedBlock;
    TranslatedBlock = new FUN[MaxSize];
    memset(TranslatedBlock, 0, sizeof (FUN) * MaxSize);
    memset(Memory, 0, sizeof (int8) * MaxSize);
}

int8 inline &getZeroPage() {
    return Memory [Memory[++PC]];
}

int8 inline &getZeroPageX() {
    return Memory[Memory[++PC] + X];
}

int8 inline &getAbsolute() {
    return Memory[Memory[++PC] + Memory[++PC] << 8];
}

int8 inline &getAbsoluteX() {
    return Memory[Memory[++PC] + Memory[++PC] << 8 + X];
}

int8 inline & getAbsoluteY() {
    return Memory [Memory[++PC] + Memory[++PC] << 8 + Y];
}

int8 inline &getIndirectX() {

    unint16 address = (unint8) Memory[++PC] + X;
    unint16 AdrressLow = Memory[Memory[address]];
    unint16 AdrressHigh = Memory[Memory[address + 1]];
    AdrressHigh = AdrressHigh << 8;
    return Memory[AdrressHigh + AdrressLow];

}

int8 inline &getIndirectY() {
    unint16 address = (unint16) Memory[++PC];
    unint16 AdrressLow = Memory[Memory[address]];
    unint16 AdrressHigh = Memory[Memory[address + 1]];
    AdrressHigh = AdrressHigh << 8;
    return Memory[AdrressHigh + AdrressLow + Y];
}

void inline jmp() {
    PC = Memory[++PC];
}

void inline push(int8 byte) {
    *SP-- = byte;
}

void inline setCarry(unint8 s, unint8 d, unint8 result) {
    if (result < s + d) {
        C = 1;
    } else C = 0;

}

void inline setOverflow(int8 s, int8 d, int8 result) {
    if (s * d > 0 && result * d < 0) {
        V = 1;
    } else V = 0;

}

void inline setNegative(int8 result) {
    if (result < 0) {
        S = 1;
    } else S = 0;
}

void inline setZero(int8 result) {
    if (result == 0)
        Z = 1;
    else Z = 0;
}

void inline addCycle(int8 c) {
    Cycles += c;
}


// <editor-fold defaultstate="collapsed" desc="ADC">

void ADC(int8 byte) {
    int8 temp = A;
    A += byte + C;
    setCarry(byte, temp, A);
    setOverflow(byte, temp, A);
    setZero(A);
    setNegative(A);
}

void ADCI() {
    int8 byte = Memory[++PC];
    ADC(byte);
    addCycle(2);
}

void ADCZeroPage() {
    int8 byte = getZeroPage();
    ADC(byte);
    addCycle(3);
}

void ADCZeroPageX() {
    int8 byte = getZeroPageX();
    ADC(byte);
    addCycle(4);
}

void ADCAbsolute() {
    int8 byte = getAbsolute();
    ADC(byte);
    addCycle(4);
}

void ADCAbsoluteX() {
    int8 byte = getAbsoluteX();
    ADC(byte);
    addCycle(4);
}

void ADCAbsoluteY() {
    int8 byte = getAbsoluteY();
    ADC(byte);
    addCycle(4);
}

void ADCIndirectX() {
    int8 byte = getIndirectX();
    ADC(byte);
    addCycle(6);
}

void ADCIndirectY() {
    int8 byte = getIndirectY();
    ADC(byte);
    addCycle(5);
}
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="AND">

void AND(int8 byte) {
    int8 temp = A;
    A = A& byte;
    setZero(A);
    setNegative(A);
}

void ANDI() {
    int8 byte = Memory[++PC];
    AND(byte);
}

void ANDZeroPage() {
    int8 byte = getZeroPage();
    AND(byte);
}

void ANDZeroPageX() {
    int8 byte = getZeroPageX();
    AND(byte);
}

void ANDAbsolute() {
    int8 byte = getAbsolute();
    AND(byte);
}

void ANDAbsoluteX() {
    int8 byte = getAbsoluteX();
    AND(byte);
}

void ANDAbsoluteY() {
    int8 byte = getAbsoluteY();
    AND(byte);
}

void ANDIndirectX() {
    int8 byte = getIndirectX();
    AND(byte);
}

void ANDIndirectY() {
    int8 byte = getIndirectY();
    AND(byte);
}
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="ASL">

void ASL() {

    C = A & 0x80;
    A = A << 1;
    setZero(A);
    setNegative(A);
    addCycle(2);
}

void ASL(int8 &byte) {
    C = byte & 0x80;
    byte = byte << 1;
    setZero(byte);
    setNegative(byte);
}

void ASLZeroPage() {
    ASL(getZeroPage());
    addCycle(5);
}

void ASLZeroPageX() {
    ASL(getZeroPageX());
    addCycle(6);
}

void ASLAbsolute() {
    ASL(getAbsolute());
    addCycle(6);

}

void ASLAbsoluteX() {
    ASL(getAbsoluteX());
    addCycle(7);
}
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="BCC">

void BCC() {

    if (C == 0) {
        jmp();
    }
    addCycle(2);
}

// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="BCS">

void BCS() {

    if (C == 1) {
        jmp();
    }
    addCycle(2);
}

// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="BEQ">

void BEQ() {

    if (Z == 1) {
        jmp();
    }
    addCycle(2);
}

// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="BMI">

void BMI() {

    if (S == 1) {
        jmp();
    }
    addCycle(2);
}

// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="BNE">

void BNE() {

    if (Z == 0) {
        jmp();
    }
    addCycle(2);
}

// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="BPL">

void BPL() {

    if (S == 0) {
        jmp();
    }
    addCycle(2);
}

// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="BRK">

void BRK() {

    push(PC >> 8);
    push(PC);
    push(S << 7 | V << 6 | B << 4 | D << 3 | I << 2 | Z << 1 | C);
    PC = Memory[0xFFFE] | Memory[0xFFFF] << 8;
    I = 1;
    addCycle(7);
}

// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="BIT">

void BIT(int8 byte) {
    S = byte & 0x80;
    V = byte & 0x40;
    setZero(A & byte);
}

void BITZeroPage() {
    BIT(getZeroPage());
    addCycle(3);
}

void BITAbsolute() {
    BIT(getAbsolute());
    addCycle(4);
}
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="BVC">

void BVC() {
    if(V==0){
        jmp();
    }
    addCycle(2);
}

// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="BVS">

void BVS() {
    if(V==1){
        jmp();
    }
    addCycle(2);
}
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="CLC">

void CLC() {
    C=0;
    addCycle(2);
}
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="CLD">

void CLD() {
    D=0;
    addCycle(2);
}
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="CLI">

void CLI() {
    I=0;
    addCycle(2);
}
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="CLV">

void CLV() {
    V=0;
    addCycle(2);
}
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="CMP">

void CMP(int8 byte) {
    int8 temp = A-byte;
    setCarry(A, -byte,temp);
    setZero(temp);
    setNegative(temp);
}

void CMPI() {
    int8 byte = Memory[++PC];
    CMP(byte);
    addCycle(2);
}

void CMPZeroPage() {
    int8 byte = getZeroPage();
    CMP(byte);
    addCycle(3);
}

void CMPZeroPageX() {
    int8 byte = getZeroPageX();
    CMP(byte);
    addCycle(4);
}

void CMPAbsolute() {
    int8 byte = getAbsolute();
   CMP(byte);
    addCycle(4);
}

void CMPAbsoluteX() {
    int8 byte = getAbsoluteX();
    CMP(byte);
    addCycle(4);
}

void CMPAbsoluteY() {
    int8 byte = getAbsoluteY();
    CMP(byte);
    addCycle(4);
}

void CMPIndirectX() {
    int8 byte = getIndirectX();
    CMP(byte);
    addCycle(6);
}

void CMPIndirectY() {
    int8 byte = getIndirectY();
    CMP(byte);
    addCycle(5);
}
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="CPX">

void CPX(int8 byte) {
    int8 temp = X-byte;
    setCarry(X, -byte,temp);
    setZero(temp);
    setNegative(temp);
}

void CPXI() {
    int8 byte = Memory[++PC];
    CPX(byte);
    addCycle(2);
}

void CPXZeroPage() {
    int8 byte = getZeroPage();
    CPX(byte);
    addCycle(3);
}

void CPXAbsolute() {
    int8 byte = getAbsolute();
    CPX(byte);
    addCycle(4);
}

// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="CPY">

void CPY(int8 byte) {
    int8 temp = Y-byte;
    setCarry(Y, -byte,temp);
    setZero(temp);
    setNegative(temp);
}

void CPYI() {
    int8 byte = Memory[++PC];
    CPX(byte);
    addCycle(2);
}

void CPYZeroPage() {
    int8 byte = getZeroPage();
     CPY(byte);
    addCycle(3);
}

void CPYAbsolute() {
    int8 byte = getAbsolute();
     CPY(byte);
    addCycle(4);
}

// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="DEC">

void DEC(int8& byte) {
    byte--;
    setZero(byte);
    setNegative(byte);
}



void DECZeroPage() {
    DEC(getZeroPage());
    
    addCycle(5);
}

void DECZeroPageX() {
    DEC(getZeroPageX());
 
    addCycle(6);
}

void DECAbsolute() {
   DEC(getAbsolute());
  
    addCycle(6);
}

void DECAbsoluteX() {
    DEC(getAbsoluteX());
    addCycle(7);
}
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="DEX">

void DEX() {
    X--;
    setZero(X);
    setNegative(X);
     addCycle(2);
}

// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="DEY">

void DEY() {
    Y--;
    setZero(Y);
    setNegative(Y);
     addCycle(2);
}

// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="EOR">

void EOR(int8 byte) {
    
    A = A^byte;
   
    setZero(A);
    setNegative(A);
}

void EORI() {
    int8 byte = Memory[++PC];
    EOR(byte);
    addCycle(2);
}

void EORZeroPage() {
    int8 byte = getZeroPage();
    EOR(byte);
    addCycle(3);
}

void EORZeroPageX() {
    int8 byte = getZeroPageX();
    EOR(byte);
    addCycle(4);
}

void EORAbsolute() {
    int8 byte = getAbsolute();
    EOR(byte);
    addCycle(4);
}

void EORAbsoluteX() {
    int8 byte = getAbsoluteX();
    EOR(byte);
    addCycle(4);
}

void EORAbsoluteY() {
    int8 byte = getAbsoluteY();
    EOR(byte);
    addCycle(4);
}

void EORIndirectX() {
    int8 byte = getIndirectX();
    EOR(byte);
    addCycle(6);
}

void EORIndirectY() {
    int8 byte = getIndirectY();
    EOR(byte);
    addCycle(5);
}
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="INC">

void INC(int8& byte) {
    byte++;
    setZero(byte);
    setNegative(byte);
}



void INCZeroPage() {
    INC(getZeroPage());
    
    addCycle(5);
}

void INCZeroPageX() {
   INC(getZeroPageX());
 
    addCycle(6);
}

void INCAbsolute() {
   INC(getAbsolute());
  
    addCycle(6);
}

void INCAbsoluteX() {
    INC(getAbsoluteX());
    addCycle(7);
}
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="INX">

void INX() {
    X++;
    setZero(X);
    setNegative(X);
    addCycle(2);
}
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="INY">

void INY() {
    Y++;
    setZero(Y);
    setNegative(Y);
    addCycle(2);
}
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="JMP">

void JMPAbsolute() {
    PC = Memory[++PC]+Memory[++PC]<<8;
    addCycle(3);
}

void JMPIndirect() {
    unint16 Address= Memory[++PC]+Memory[++PC]<<8;
    PC = Memory[Address]+Memory[Address+1]<<8;
    addCycle(5);
}
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="JSR">

void JSR() {
    push(PC >> 8);
    push(PC);
    PC = Memory[++PC]+Memory[++PC]<<8;
    addCycle(6);
}
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="LDA">

void LDA(int8 byte) {
    A = byte;  
    setZero(A);
    setNegative(A);
}

void LDAI() {
    int8 byte = Memory[++PC];
    LDA(byte);
    addCycle(2);
}

void LDAZeroPage() {
    int8 byte = getZeroPage();
    LDA(byte);
    addCycle(3);
}

void LDAZeroPageX() {
    int8 byte = getZeroPageX();
    LDA(byte);
    addCycle(4);
}

void LDAAbsolute() {
    int8 byte = getAbsolute();
    LDA(byte);
    addCycle(4);
}

void LDAAbsoluteX() {
    int8 byte = getAbsoluteX();
    LDA(byte);
    addCycle(4);
}

void LDAAbsoluteY() {
    int8 byte = getAbsoluteY();
    LDA(byte);
    addCycle(4);
}

void LDAIndirectX() {
    int8 byte = getIndirectX();
    LDA(byte);
    addCycle(6);
}

void LDAIndirectY() {
    int8 byte = getIndirectY();
    LDA(byte);
    addCycle(5);
}
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="LDX">

void LDX(int8 byte) {
    X=byte;
    setZero(X);
    setNegative(X);
}

void LDXI() {
    int8 byte = Memory[++PC];
    LDX(byte);
    addCycle(2);
}

void LDXZeroPage() {
    int8 byte = getZeroPage();
    LDX(byte);
    addCycle(3);
}

void LDXZeroPageX() {
    int8 byte = getZeroPageX();
    LDX(byte);
    addCycle(4);
}

void LDXAbsolute() {
    int8 byte = getAbsolute();
    LDX(byte);
    addCycle(4);
}

void LDXAbsoluteX() {
    int8 byte = getAbsoluteX();
    LDX(byte);
    addCycle(4);
}


// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="LDY">

void LDY(int8 byte) {
    Y=byte;
    setZero(Y);
    setNegative(Y);
}

void LDYI() {
    int8 byte = Memory[++PC];
    LDY(byte);
    addCycle(2);
}

void LDYZeroPage() {
    int8 byte = getZeroPage();
   LDY(byte);
    addCycle(3);
}

void LDYZeroPageX() {
    int8 byte = getZeroPageX();
    LDY(byte);
    addCycle(4);
}

void LDYAbsolute() {
    int8 byte = getAbsolute();
    LDY(byte);
    addCycle(4);
}

void LDYAbsoluteX() {
    int8 byte = getAbsoluteX();
    LDY(byte);
    addCycle(4);
}


// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="LSR">
void LSR(int8& byte) {
    C = byte&0x01;
    byte=byte>>1;
    setZero(byte);
}

void LSR() {
    LSR(C);
    addCycle(2);
}

void LSRZeroPage() {
    
   LSR(getZeroPage());
    addCycle(5);
}

void LSRZeroPageX() {
    LSR(getZeroPageX());
    addCycle(6);
}

void LSRAbsolute() {
    LSR(getAbsolute());
    addCycle(6);
}

void LSRAbsoluteX() {
    LSR(getAbsoluteX());
    addCycle(7);
}


// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="NOP">
void NOP() {
   addCycle(2);
}
// </editor-fold>
void translateOP(unint8 op) {
    int PC_temp = PC;
    switch (op) {
            // <editor-fold defaultstate="collapsed" desc="tanslate ADC">
        case 0x69:
            TranslatedBlock[PC] = &ADCI;
            break;
        case 0x65:
            TranslatedBlock[PC] = &ADCZeroPage;
            break;
        case 0x75:
            TranslatedBlock[PC] = &ADCZeroPageX;
            break;
        case 0x6D:
            TranslatedBlock[PC] = &ADCAbsolute;
            break;
        case 0x7D:
            TranslatedBlock[PC] = &ADCAbsoluteX;
            break;
        case 0x79:
            TranslatedBlock[PC] = &ADCAbsoluteY;
            break;
        case 0x61:
            TranslatedBlock[PC] = &ADCIndirectX;
            break;
        case 0x71:
            TranslatedBlock[PC] = &ADCIndirectY;
            break;
            // </editor-fold>

            // <editor-fold defaultstate="collapsed" desc="tanslate AND">
        case 0x29:
            TranslatedBlock[PC] = &ANDI;
            break;
        case 0x25:
            TranslatedBlock[PC] = &ANDZeroPage;
            break;
        case 0x35:
            TranslatedBlock[PC] = &ANDZeroPageX;
            break;
        case 0x2D:
            TranslatedBlock[PC] = &ANDAbsolute;
            break;
        case 0x3D:
            TranslatedBlock[PC] = &ANDAbsoluteX;
            break;
        case 0x39:
            TranslatedBlock[PC] = &ANDAbsoluteY;
            break;
        case 0x21:
            TranslatedBlock[PC] = &ANDIndirectX;
            break;
        case 0x31:
            TranslatedBlock[PC] = &ANDIndirectY;
            break;
            // </editor-fold>

            // <editor-fold defaultstate="collapsed" desc="tanslate ASL">
        case 0x0A:
            TranslatedBlock[PC] = &ASL;
            break;
        case 0x06:
            TranslatedBlock[PC] = &ASLZeroPage;
            break;
        case 0x16:
            TranslatedBlock[PC] = &ASLZeroPageX;
            break;
        case 0x0E:
            TranslatedBlock[PC] = &ASLAbsolute;
            break;
        case 0x1E:
            TranslatedBlock[PC] = &ASLAbsoluteX;
            break;

            // </editor-fold>

            // <editor-fold defaultstate="collapsed" desc="tanslate BCC">
        case 0x90:
            TranslatedBlock[PC] = &BCC;
            break;

            // </editor-fold>

            // <editor-fold defaultstate="collapsed" desc="tanslate BCS">
        case 0xB0:
            TranslatedBlock[PC] = &BCS;
            break;

            // </editor-fold>

            // <editor-fold defaultstate="collapsed" desc="tanslate BEQ">
        case 0xF0:
            TranslatedBlock[PC] = &BEQ;
            break;


            // </editor-fold>

            // <editor-fold defaultstate="collapsed" desc="tanslate BIT">
        case 0x24:
            TranslatedBlock[PC] = &BITZeroPage;
            break;
        case 0x2C:
            TranslatedBlock[PC] = &BITAbsolute;
            break;

            // </editor-fold>

            // <editor-fold defaultstate="collapsed" desc="tanslate BMI">
        case 0x30:
            TranslatedBlock[PC] = &BMI;
            break;


            // </editor-fold>

            // <editor-fold defaultstate="collapsed" desc="tanslate BNE">
        case 0xD0:
            TranslatedBlock[PC] = &BNE;
            break;


            // </editor-fold>

            // <editor-fold defaultstate="collapsed" desc="tanslate BPL">
        case 0x10:
            TranslatedBlock[PC] = &BPL;
            break;


            // </editor-fold>

            // <editor-fold defaultstate="collapsed" desc="tanslate BRK">
        case 0x00:
            TranslatedBlock[PC] = &BRK;
            break;


            // </editor-fold>
            
            // <editor-fold defaultstate="collapsed" desc="tanslate BVC">
        case 0x50:
            TranslatedBlock[PC] = &BVC;
            break;


            // </editor-fold>
            
            // <editor-fold defaultstate="collapsed" desc="tanslate BVS">
        case 0x70:
            TranslatedBlock[PC] = &BVS;
            break;


            // </editor-fold>
            
            // <editor-fold defaultstate="collapsed" desc="tanslate CLC">
        case 0x18:
            TranslatedBlock[PC] = &CLC;
            break;


            // </editor-fold>
            
            // <editor-fold defaultstate="collapsed" desc="tanslate CLD">
        case 0xD8:
            TranslatedBlock[PC] = &CLD;
            break;


            // </editor-fold>
            
            // <editor-fold defaultstate="collapsed" desc="tanslate CLI">
        case 0x58:
            TranslatedBlock[PC] = &CLI;
            break;


            // </editor-fold>
            
            // <editor-fold defaultstate="collapsed" desc="tanslate CMP">
        case 0xC9:
            TranslatedBlock[PC] = &CMPI;
            break;
        case 0xC5:
            TranslatedBlock[PC] = &CMPZeroPage;
            break;
        case 0xD5:
            TranslatedBlock[PC] = &CMPZeroPageX;
            break;
        case 0xCD:
            TranslatedBlock[PC] = &CMPAbsolute;
            break;
        case 0xDD:
            TranslatedBlock[PC] = &CMPAbsoluteX;
            break;
        case 0xD9:
            TranslatedBlock[PC] = &CMPAbsoluteY;
            break;
        case 0xC1:
            TranslatedBlock[PC] = &CMPIndirectX;
            break;
        case 0xD1:
            TranslatedBlock[PC] = &CMPIndirectY;
            break;
            // </editor-fold>
              // <editor-fold defaultstate="collapsed" desc="tanslate CPX">
        case 0xE0:
            TranslatedBlock[PC] = &CPXI;
            break;
        case 0xE4:
            TranslatedBlock[PC] = &CPXZeroPage;
            break;
        case 0xEC:
            TranslatedBlock[PC] = &CPXAbsolute;
            break;
            // </editor-fold>
             // <editor-fold defaultstate="collapsed" desc="tanslate CPY">
        case 0xC0:
            TranslatedBlock[PC] = &CPYI;
            break;
        case 0xC4:
            TranslatedBlock[PC] = &CPYZeroPage;
            break;
        case 0xCC:
            TranslatedBlock[PC] = &CPYAbsolute;
            break;
        
            // </editor-fold>
        default:cout << "Unknown Op" << endl;
    }
    FUN Operation = TranslatedBlock[PC_temp];
    Operation();
}
#endif	/* CPU6502_H */

